package com.xayy.health.core.config.datasource;

import cn.hutool.core.collection.CollUtil;
import cn.kawins.base.result.MsgPrefixKit;
import cn.kawins.mybatis.MybatisConstant;
import cn.kawins.mybatis.MybatisSysParam;
import cn.kawins.mybatis.interceptor.AutoFillingInterceptor;
import cn.kawins.spring.store.db.DbConstant;
import com.github.pagehelper.autoconfigure.PageHelperProperties;
import com.xayy.health.core.constant.HealthManagerConstant;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import tk.mybatis.mapper.entity.Config;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.session.Configuration;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 多数据源配置
 *
 * @author dongxingli
 * createTime 2021/2/25
 * @version V1.0
 */
@Slf4j
@SpringBootConfiguration
@ConditionalOnProperty(prefix = "datasource", name = "enabled", havingValue = "true", matchIfMissing = true)
public class MultipleDatasourceConfig {

    public static final String MAPPER_HELPER_BEAN_NAME = "mapperHelper";

    private static final ResourcePatternResolver RESOLVER = new PathMatchingResourcePatternResolver();

    /**
     * 通用Mapper默认配置
     *
     * @param mybatisSysParam
     * @return
     */
    @Primary
    @Bean(name = MAPPER_HELPER_BEAN_NAME)
    public MapperHelper mapperHelper(MybatisSysParam mybatisSysParam) {
        Config config = mybatisSysParam.getMapper();
        if (config == null) {
            config = new Config();
            config.setMappers(MybatisConstant.DEFAULT_MAPPERS);
            config.setIdentity(MybatisConstant.DEFAULT_IDENTITY);
            config.setBefore(MybatisConstant.DEFAULT_BEFORE);
            config.setNotEmpty(MybatisConstant.DEFAULT_NOT_EMPTY);
            config.setSafeDelete(MybatisConstant.DEFAULT_SAFE_DELETE);
            config.setSafeUpdate(MybatisConstant.DEFAULT_SAFE_UPDATE);
            config.setEnableMethodAnnotation(MybatisConstant.DEFAULT_ENABLE_METHOD_ANNOTATION);
            config.setCheckExampleEntityClass(MybatisConstant.DEFAULT_CHECK_EXAMPLE_ENTITY_CLASS);
        }
        MapperHelper mapperHelper = new MapperHelper();
        mapperHelper.setConfig(config);
        log.debug(MsgPrefixKit.gen("通用Mapper配置加载完成"));
        return mapperHelper;
    }

    /**
     * 动态数据源配置
     *
     * @return
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(DataSourceParam dataSourceParam, DefaultListableBeanFactory defaultListableBeanFactory) {
        List<DataSourceProperties> list = dataSourceParam.getList();
        if (CollUtil.isEmpty(list)) {
            throw new RuntimeException("数据源不能为空");
        }
        Map<Object, Object> dataSourcesMap = new HashMap<>(2);
        list.forEach(x -> {
            HikariDataSource hikariDataSource = new HikariDataSource();
            String[] split = x.getJdbcUrl().split("\\?");
            if (x.getSourceName().toLowerCase().contains(HealthManagerConstant.MYSQL)) {
                hikariDataSource.setJdbcUrl(split[0] + DbConstant.MYSQL_URL_SUFFIX);
                log.info(x.getSourceName() + "数据源配置：" + split[0] + DbConstant.MYSQL_URL_SUFFIX);
            } else {
                log.info(x.getSourceName() + "数据源配置：" + x.getJdbcUrl());
                hikariDataSource.setJdbcUrl(x.getJdbcUrl());
            }
            if (x.getDriverClass() != null) {
                hikariDataSource.setDriverClassName(x.getDriverClass());
            }
            if (x.getSourceName().toLowerCase().contains(HealthManagerConstant.LINDORM)) {
                hikariDataSource.addDataSourceProperty("database", x.getDatabase());
            }
            hikariDataSource.setUsername(x.getUsername());
            hikariDataSource.setPassword(x.getPassword());
            defaultListableBeanFactory.registerSingleton(x.getSourceName(), hikariDataSource);
            dataSourcesMap.put(x.getSourceName(), hikariDataSource);
        });
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(dataSourcesMap);
        dynamicDataSource.setDefaultTargetDataSource(dataSourcesMap.get(list.get(0).getSourceName()));
        return dynamicDataSource;
    }

    /**
     * 默认的Mybatis配置
     *
     * @param mybatisSysParam
     * @param multipleDataSource
     * @return
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(MybatisSysParam mybatisSysParam, DataSource multipleDataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(multipleDataSource);
        Configuration configuration = new Configuration();
        configuration.addInterceptor(new AutoFillingInterceptor());
        configuration.setMapUnderscoreToCamelCase(true);
        if (mybatisSysParam.getShowSql()) {
            configuration.setLogImpl(StdOutImpl.class);
        }
        sqlSessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactoryBean.setMapperLocations(getResources(mybatisSysParam.getMapperLocations()));
        log.debug(MsgPrefixKit.gen("Mybatis配置加载完成"));
        return sqlSessionFactoryBean;
    }

    @Bean
    @Primary
    public PageHelperProperties properties(MybatisSysParam mybatisSysParam) {
        PageHelperProperties properties = mybatisSysParam.getPageHelper();
        if (properties == null) {
            properties = new PageHelperProperties();
            properties.setReasonable(MybatisConstant.DEFAULT_REASONABLE);
            properties.setAutoRuntimeDialect(MybatisConstant.DEFAULT_AUTO_RUNTIME_DIALECT);
            properties.setSupportMethodsArguments(MybatisConstant.DEFAULT_SUPPORT_METHODS_ARGUMENTS);
            properties.setParams(MybatisConstant.DEFAULT_PARAMS);
        }
        return properties;
    }

    private Resource[] getResources(String location) {
        try {
            return RESOLVER.getResources(location);
        } catch (IOException e) {
            return new Resource[0];
        }
    }
}
